package cn.accjiyun.pki.security;

import cn.accjiyun.pki.common.utils.BytesHexConverter;
import cn.accjiyun.pki.entity.CAConfig;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.io.FileInputStream;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;

public class RSAUtils {

    public static void main(String[] args) {

        String path = "d:/test/";
        //证书属性类
        CAConfig caConfig = new CAConfig();
        //获取公钥

        String cerPath = "D:/pki/root.cer";
        String pfxPath = "D:/pki/root.pfx";
        try {
            //证书工厂
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            //读取公共证书文件
            FileInputStream fileInputstream = new FileInputStream(cerPath);
            //载入证书文件
            X509Certificate x509certificate = (X509Certificate) certificateFactory.generateCertificate(fileInputstream);
            //读取公钥
            PublicKey rootPublicKey = x509certificate.getPublicKey();
            //从KeyStore中获取PKCS12实例
            KeyStore keyStore = KeyStore.getInstance(CAConfig.PKCS12);
            //读取私有证书文件
            fileInputstream = new FileInputStream(pfxPath);
            keyStore.load(fileInputstream, caConfig.getPK_PASSWORD().toCharArray());
            fileInputstream.close();
            PrivateKey rootPrivateKey = (PrivateKey) keyStore.getKey(caConfig.getPK_ALIAS(), caConfig.getPK_PASSWORD().toCharArray());
            //私钥加密 公钥解密


            String content = "123";
            //生成AES密钥
            byte[] AESKeyBytes = AESUtil.initkey();

            //RSA私钥加密AES密钥
            byte[] cipherKeyBytes = RSAUtils.encryptByRSA(rootPublicKey.getEncoded(), AESKeyBytes);
            //加密文本
            byte[] cipherTextBytes = AESUtil.encrypt(content.getBytes(), AESKeyBytes);
            //签名
            byte[] signBytes = RSAUtils.sign(rootPrivateKey.getEncoded(), RSAUtils.MdigestSHA(cipherTextBytes));

            String HexAESKey = BytesHexConverter.Bytes2HexString(cipherKeyBytes);
            String HexCipherText = BytesHexConverter.Bytes2HexString(cipherTextBytes);
            String HexSign = BytesHexConverter.Bytes2HexString(signBytes);

            cipherKeyBytes = BytesHexConverter.HexString2Bytes(HexAESKey);
            AESKeyBytes = RSAUtils.decryptByRSAPrivateKey(rootPrivateKey.getEncoded(), cipherKeyBytes);
            cipherTextBytes = BytesHexConverter.HexString2Bytes(HexCipherText);
            byte[] plainByte = AESUtil.decrypt(cipherTextBytes, AESKeyBytes);
            String plainText = new String(plainByte);
            System.out.println(plainText);

//            //获得摘要
//            byte[] source = MdigestSHA("假户数据");
//            //使用私钥对摘要进行加密 获得密文 即数字签名
//            byte[] sign = sign(rootPrivateKey.getEncoded(), source);
//
//            //使用公钥对密文进行解密,解密后与摘要进行匹配
//            boolean isValid = verify(rootPublicKey.getEncoded(), source, sign);
//            if (isValid) {
//                System.out.println("匹配成功 合法的签名!");
//            }
//            //公钥加密 私钥解密
//            source = MdigestSHA("假户数据");
//            sign = encryptByRSA(rootPublicKey.getEncoded(), source);
//            if (Arrays.equals(source, decryptByRSAPrivateKey(rootPrivateKey.getEncoded(), sign))) {
//                System.out.println("匹配成功");
//            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取cer证书
     *
     * @param certPath cer证书路径
     */
    public static PublicKey readCer(String certPath) {
        PublicKey publicKey = null;
        try {
            //证书工厂
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            //读取公共证书文件
            FileInputStream fileInputStream = new FileInputStream(certPath);
            //载入证书文件
            X509Certificate x509certificate = (X509Certificate) certificateFactory.generateCertificate(fileInputStream);
            //类型
            String type = x509certificate.getType();
            //版本
            String version = x509certificate.getVersion() + "";
            //标题
            String title = x509certificate.getSubjectDN().getName();
            //得到开始有效日期
            String notBefore = x509certificate.getNotBefore().toString();
            //得到截止日期
            String notAfter = x509certificate.getNotAfter().toString();
            //得到序列号
            String serialNumber = x509certificate.getSerialNumber().toString(16);
            //得到发行者名
            String issuerDN = x509certificate.getIssuerDN().getName();
            //得到签名算法
            String sigAlgName = x509certificate.getSigAlgName();
            //得到公钥算法
            String algorithm = x509certificate.getPublicKey().getAlgorithm();
            //获取公钥
            publicKey = x509certificate.getPublicKey();

            System.out.println("public key:" + new String(Base64.encodeBase64(x509certificate.getPublicKey().getEncoded())));
            fileInputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return publicKey;
    }

    /**
     * 读取pfx证书
     *
     * @param path pfx证书路径
     */
    public static PrivateKey readPKCS12(String path, CAConfig caConfig) {
        PrivateKey privateKey = null;
        try {
            //中获取PKCS12 KeyStore实例
            KeyStore keyStore = KeyStore.getInstance(CAConfig.PKCS12);
            //读取私有证书文件
            FileInputStream fileInputStream = new FileInputStream(path);
            //获取密钥密码
            char[] password = caConfig.getPK_PASSWORD().toCharArray();
            //读取私钥
            keyStore.load(fileInputStream, password);
            //关闭文件输入流
            fileInputStream.close();
            //读取私钥
            privateKey = (PrivateKey) keyStore.getKey(caConfig.getPK_ALIAS(), password);
            //获取公钥
            Certificate certificate = keyStore.getCertificate(caConfig.getPK_ALIAS());
            PublicKey publicKey = certificate.getPublicKey();

            System.out.println("public key=" + new String(Base64.encodeBase64(publicKey.getEncoded())));
        } catch (Exception e) {
            System.out.println(e);
        }
        return privateKey;
    }

    /**
     * 使用RSA公钥加密数据
     *
     * @param pubKeyInByte 打包的byte[]形式公钥
     * @param data         要加密的数据
     * @return 加密数据
     */
    public static byte[] encryptByRSA(byte[] pubKeyInByte, byte[] data) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubKeyInByte);
            PublicKey publicKey = keyFactory.generatePublic(pubSpec);
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return cipher.doFinal(data);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 使用RSA私钥加密数据
     *
     * @param privKeyInByte 打包的byte[]形式私钥
     * @param data          要加密的数据
     * @return 加密数据
     */
    public static byte[] encryptByRSA1(byte[] privKeyInByte, byte[] data) {
        try {
            PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(
                    privKeyInByte);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(privSpec);
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            return cipher.doFinal(data);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 用RSA公钥解密
     *
     * @param publicKeyInByte 公钥打包成byte[]形式
     * @param data            要解密的数据
     * @return 解密数据
     */
    public static byte[] decryptByRSAPublicKey(byte[] publicKeyInByte, byte[] data) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(publicKeyInByte);
            PublicKey pubKey = keyFactory.generatePublic(pubSpec);
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, pubKey);
            return cipher.doFinal(data);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 用RSA私钥解密
     *
     * @param privateKeyInByte 私钥打包成byte[]形式
     * @param data             要解密的数据
     * @return 解密数据
     */
    public static byte[] decryptByRSAPrivateKey(byte[] privateKeyInByte, byte[] data) {
        try {
            PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(
                    privateKeyInByte);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(privSpec);
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return cipher.doFinal(data);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * SHA摘要
     *
     * @param source
     * @return
     */
    public static byte[] MdigestSHA(String source) {
        try {
            MessageDigest thisMD = MessageDigest.getInstance(CAConfig.HASH);
            byte[] digest = thisMD.digest(source.getBytes("UTF-8"));
            return digest;
        } catch (Exception e) {
            return null;
        }
    }

    public static byte[] MdigestSHA(byte[] source) {
        try {
            MessageDigest thisMD = MessageDigest.getInstance(CAConfig.HASH);
            byte[] digest = thisMD.digest(source);
            return digest;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 使用私钥加密数据
     * 用一个已打包成byte[]形式的私钥加密数据，即数字签名
     *
     * @param privateKeyInByte 打包成byte[]的私钥
     * @param source           要签名的数据，一般应是数字摘要
     * @return 签名 byte[]
     */
    public static byte[] sign(byte[] privateKeyInByte, byte[] source) {
        try {
            PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(privateKeyInByte);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(privateSpec);
            Signature signature = Signature.getInstance(CAConfig.SHA1WITHRSA);
            signature.initSign(privateKey);
            signature.update(source);
            return signature.sign();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 验证签名
     *
     * @param privateKeyInByte 二进制私钥
     * @param source           源数据
     * @param sign             数字签名
     * @return 验证结果
     */
    public static boolean verify(byte[] privateKeyInByte, byte[] source, byte[] sign) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(CAConfig.ALGORITHM);
            Signature signature = Signature.getInstance(CAConfig.SHA1WITHRSA);
            X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(privateKeyInByte);
            PublicKey publicKey = keyFactory.generatePublic(pubSpec);
            signature.initVerify(publicKey);
            signature.update(source);
            return signature.verify(sign);
        } catch (Exception e) {
            return false;
        }
    }

}